Conversation
Replace ESLint with oxlint for ~430x faster linting (30s → 70ms). Follows the same pattern as sentry-javascript#19134. Changes: - Add .oxlintrc.json with rule parity to the previous ESLint config - Use jsPlugins for @sentry-internal/eslint-plugin-sdk custom rules - Migrate all eslint-disable comments to oxlint-disable format - Remove unused disable directives (rules already off in config) - Remove ESLint dependencies and config files - Clean up ESLint-related yarn resolutions Known gaps (same as sentry-javascript): - No import sorting (simple-import-sort has no oxlint equivalent) - No naming-convention, member-ordering, explicit-member-accessibility - sdk/no-regexp-constructor disabled (inline disable not supported for jsPlugin rules) Closes #5615 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Semver Impact of This PR⚪ None (no version bump detected) 📋 Changelog PreviewThis is how your changes will appear in the changelog.
🤖 This preview updates automatically when you update the PR. |
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Removing ESLint from packages/core changed the dependency resolution tree, causing the expo sample to resolve es-iterator-helpers@1.0.19 which has a known incompatibility with eslint-plugin-react. Pinning to ^1.2.1 resolves the issue. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove ESLint from all sample apps (react-native, react-native-macos, expo) and performance test apps (TestAppPlain, TestAppSentry). Replace with lightweight oxlint configs for samples that had lint scripts. Also clean up root package.json: - Remove es-iterator-helpers resolution (no longer needed) - Remove eslint-plugin-ft-flow resolution - Remove @typescript-eslint/typescript-estree minimatch resolutions This fully removes ESLint from the repository. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Update dependabot.yml: replace typescript-eslint group with oxlint - Update VS Code extensions: replace vscode-eslint with oxlint extension - Update update-javascript.sh: remove @sentry-internal/eslint-config-sdk - Remove stale eslint-disable comments from sample apps - Update comments referencing ESLint in SDK source files - Downgrade react-hooks/exhaustive-deps to warn in sample configs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sample apps reference `npx oxlint` but oxlint was only installed in packages/core. With yarn PnP, the binary isn't available to sibling workspaces. Adding it to root devDependencies makes it available to all workspaces. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Removing direct @typescript-eslint v8 dependencies caused the transitive @typescript-eslint/typescript-estree@6.21.0 (from eslint-plugin-sdk) to resolve minimatch@9.0.3 which has known ReDoS vulnerabilities. Pin to ^9.0.7 to resolve. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Android (legacy) Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 4a17c8f+dirty | 406.62 ms | 400.58 ms | -6.04 ms |
| df1f7df+dirty | 442.64 ms | 427.16 ms | -15.48 ms |
| a483f9f+dirty | 396.82 ms | 453.28 ms | 56.46 ms |
| 60cd796+dirty | 445.84 ms | 492.45 ms | 46.61 ms |
| 5c16cdc+dirty | 423.48 ms | 452.35 ms | 28.88 ms |
| 80e4616+dirty | 411.58 ms | 462.12 ms | 50.54 ms |
| 55b77fc+dirty | 411.87 ms | 417.16 ms | 5.29 ms |
| bca62c0+dirty | 414.36 ms | 451.06 ms | 36.70 ms |
| 0b64753+dirty | 448.67 ms | 474.61 ms | 25.94 ms |
| 4e6d7d7+dirty | 480.73 ms | 515.73 ms | 35.00 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 4a17c8f+dirty | 43.75 MiB | 47.99 MiB | 4.24 MiB |
| df1f7df+dirty | 43.75 MiB | 48.08 MiB | 4.33 MiB |
| a483f9f+dirty | 43.75 MiB | 48.41 MiB | 4.66 MiB |
| 60cd796+dirty | 43.75 MiB | 48.07 MiB | 4.32 MiB |
| 5c16cdc+dirty | 17.75 MiB | 19.68 MiB | 1.94 MiB |
| 80e4616+dirty | 43.75 MiB | 48.55 MiB | 4.80 MiB |
| 55b77fc+dirty | 43.75 MiB | 47.99 MiB | 4.24 MiB |
| bca62c0+dirty | 43.75 MiB | 48.41 MiB | 4.66 MiB |
| 0b64753+dirty | 17.75 MiB | 19.70 MiB | 1.95 MiB |
| 4e6d7d7+dirty | 43.75 MiB | 48.40 MiB | 4.64 MiB |
iOS (legacy) Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| ea3e26e+dirty | 1229.13 ms | 1228.46 ms | -0.67 ms |
| 80e4616+dirty | 1221.32 ms | 1225.64 ms | 4.32 ms |
| 818a608+dirty | 1205.76 ms | 1208.00 ms | 2.24 ms |
| 77061ed+dirty | 1233.16 ms | 1234.88 ms | 1.71 ms |
| bef3709+dirty | 1222.07 ms | 1220.24 ms | -1.83 ms |
| a206511+dirty | 1185.00 ms | 1186.35 ms | 1.35 ms |
| 74979ac+dirty | 1210.49 ms | 1213.31 ms | 2.82 ms |
| a2bb688+dirty | 1223.53 ms | 1232.90 ms | 9.37 ms |
| 8a868fe+dirty | 1221.50 ms | 1230.78 ms | 9.28 ms |
| d590428+dirty | 1211.77 ms | 1220.51 ms | 8.75 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| ea3e26e+dirty | 3.41 MiB | 4.58 MiB | 1.17 MiB |
| 80e4616+dirty | 3.38 MiB | 4.60 MiB | 1.22 MiB |
| 818a608+dirty | 2.63 MiB | 3.91 MiB | 1.28 MiB |
| 77061ed+dirty | 2.63 MiB | 3.98 MiB | 1.34 MiB |
| bef3709+dirty | 3.38 MiB | 4.78 MiB | 1.40 MiB |
| a206511+dirty | 3.41 MiB | 4.67 MiB | 1.25 MiB |
| 74979ac+dirty | 3.38 MiB | 4.60 MiB | 1.22 MiB |
| a2bb688+dirty | 2.63 MiB | 3.99 MiB | 1.36 MiB |
| 8a868fe+dirty | 3.38 MiB | 4.60 MiB | 1.22 MiB |
| d590428+dirty | 3.38 MiB | 4.78 MiB | 1.39 MiB |
Android (new) Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 70250df+dirty | 418.08 ms | 480.84 ms | 62.76 ms |
| 8d89cc9+dirty | 357.69 ms | 415.79 ms | 58.10 ms |
| 1853710+dirty | 360.67 ms | 396.28 ms | 35.61 ms |
| 55b77fc+dirty | 410.46 ms | 414.11 ms | 3.65 ms |
| 69602ce+dirty | 375.37 ms | 405.28 ms | 29.91 ms |
| c1573b3+dirty | 355.65 ms | 448.82 ms | 93.17 ms |
| 90afdd3+dirty | 367.79 ms | 404.84 ms | 37.05 ms |
| 955f2eb+dirty | 388.13 ms | 433.56 ms | 45.44 ms |
| 80e4616+dirty | 427.31 ms | 461.15 ms | 33.84 ms |
| 276d348+dirty | 356.30 ms | 405.27 ms | 48.97 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 70250df+dirty | 43.94 MiB | 48.91 MiB | 4.97 MiB |
| 8d89cc9+dirty | 7.15 MiB | 8.41 MiB | 1.26 MiB |
| 1853710+dirty | 7.15 MiB | 8.41 MiB | 1.26 MiB |
| 55b77fc+dirty | 43.94 MiB | 48.82 MiB | 4.88 MiB |
| 69602ce+dirty | 7.15 MiB | 8.41 MiB | 1.26 MiB |
| c1573b3+dirty | 7.15 MiB | 8.42 MiB | 1.27 MiB |
| 90afdd3+dirty | 7.15 MiB | 8.43 MiB | 1.28 MiB |
| 955f2eb+dirty | 7.15 MiB | 8.42 MiB | 1.27 MiB |
| 80e4616+dirty | 43.94 MiB | 49.38 MiB | 5.44 MiB |
| 276d348+dirty | 7.15 MiB | 8.42 MiB | 1.26 MiB |
…s to error The big-data.test.ts file contained only a skipped test with no running assertions. Removing it and promoting sdk/no-skipped-tests back to error since there are no more intentional test.skip usages. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Now that TypeScript is upgraded to 5.9.3, enable type-aware linting via oxlint --type-aware + oxlint-tsgolint. This enforces rules like no-floating-promises, unbound-method, and no-unsafe-member-access that require type information. - Add oxlint-tsgolint devDependency - Add tsconfig.lint.json for tsgolint (avoids alwaysStrict compat issue with @sentry-internal/typescript tsconfig) - Enable --type-aware and --deny-warnings in lint scripts - Disable await-thenable (not in original ESLint config, false positives on mocks) - Add oxlint-disable comments for intentional violations Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
express@4.19.2 is no longer in the dependency tree. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The merge with main lost the !__DEV__ condition in the __SENTRY_OPTIONS__ check, which would have changed behavior in dev builds by skipping native auto-initialization. Restore the guard and update tests to match main's version that properly tests dev vs release build behavior. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
| dsn: this.getDsn(), | ||
| tunnel: undefined, | ||
| }); | ||
| // eslint-disable-next-line @typescript-eslint/no-floating-promises |
There was a problem hiding this comment.
Missing oxlint-disable for intentional floating promise
Low Severity
The eslint-disable-next-line @typescript-eslint/no-floating-promises comment was removed from this.sendEnvelope(envelope) without being migrated to an oxlint-disable-next-line equivalent. sendEnvelope returns PromiseLike<TransportMakeRequestResponse>, making this an intentional fire-and-forget floating promise. Other identical patterns in the same PR were properly migrated (e.g., reactnativeprofiler.tsx line 60, modal.tsx line 426). The typescript/no-floating-promises rule is configured as "error" for TS files in .oxlintrc.json.


📢 Type of change
📜 Description
Fully replace ESLint with oxlint across the entire repository. Follows the same migration pattern as sentry-javascript#19134.
Pros
.oxlintrc.jsonvs.eslintrc.jsextending shared configs with multiple overridesESLINT_USE_FLAT_CONFIG=falsehack; ESLint 10 will drop legacy config supportyarn.lockminimatch,ajv, and other resolutions that were pinned purely to fix vulnerabilities in ESLint's dependency treeoxlint-tsgolintprovides type-aware rules (no-floating-promises,unbound-method,no-unsafe-member-access, etc.) with zero warnings, zero lint errors@sentry-internal/eslint-plugin-sdkworks via oxlint'sjsPlugins(no-eq-empty,no-focused-tests,no-skipped-tests, etc.)Cons / known gaps
simple-import-sort/importsoxfmtsupports import sorting — can recover if we switch from prettier to oxfmt@typescript-eslint/naming-convention_prefix on private members@typescript-eslint/member-ordering@typescript-eslint/explicit-member-accessibility@typescript-eslint/typedef@typescript-eslint/unified-signaturessdk/no-regexp-constructorinline disablealwaysStrictdiagnostic@sentry-internal/typescripttsconfig setsalwaysStrict: falsewhich is removed in TS 7.0/tsgolint--deny-warningsWhat changed
SDK (
packages/core):.oxlintrc.jsonwith rule parity to the previous ESLint configjsPlugins(experimental) for@sentry-internal/eslint-plugin-sdkcustom ruleseslint-disableinline comments tooxlint-disableformat.eslintrc.js,.eslintignore)big-data.test.ts(contained only a skipped test with no running assertions)--type-awareflag +oxlint-tsgolint(enforcesno-floating-promises,unbound-method,no-unsafe-member-access, etc.)baseUrlfromtsconfig.build.json(deprecated in TS 7.0, not needed withpathsin TS 5.x+)oxlint-disablecomments for intentionalno-unsafe-member-accessviolations in plugin/vendor codeSample apps & perf tests:
.oxlintrc.jsoninsamples/react-native,samples/react-native-macos,samples/expoperformance-tests/TestAppPlain,performance-tests/TestAppSentryeslint-disablecomments from sample app source filesRepo-wide cleanup:
eslint,@typescript-eslint/*,eslint-plugin-react,eslint-plugin-react-native,eslint-config-expo, etc.)oxlintto rootdevDependenciesfor workspace-wide availabilityajv,minimatch,eslint-plugin-*that were only needed by ESLint's dependency tree)dependabot.yml: replacedtypescript-eslintgroup withoxlintvscode-eslintwithoxlintscripts/update-javascript.sh: removed@sentry-internal/eslint-config-sdkRemaining
eslintreferences (all justified)@sentry-internal/eslint-plugin-sdk— the custom Sentry lint plugin, loaded via oxlint'sjsPluginsoxlint-disable eslint(...)/typescript-eslint(...)— oxlint's rule naming conventionyarn.locktransitive deps — from@sentry-internal/eslint-plugin-sdkand expo/RN ecosystem💡 Motivation and Context
ESLINT_USE_FLAT_CONFIG=falsewas a dead end — ESLint 10 drops legacy configCloses #5615
💚 How did you test it?
yarn lintpasses (all 4 lerna projects + native linters)yarn buildpasseseslint-disablecomments in source📝 Checklist
sendDefaultPIIis enabled🔮 Next steps
oxfmtwhich has built-in import sorting.naming-convention,member-ordering,explicit-member-accessibilitymay become available. These are the main remaining gaps where automated enforcement is lost.sdk/no-regexp-constructor.alwaysStrictworkaround — Once@sentry-internal/typescriptdrops thealwaysStrictoption, the lint script wrapper can be simplified.